package ru.ssau.berlin.jsf.beans;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;
import java.util.logging.Logger;
import javax.ejb.EJB;
import ru.ssau.berlin.ejb.facade.EventFacadeLocal;
import ru.ssau.berlin.ejb.facade.GroupFacadeLocal;
import ru.ssau.berlin.ejb.facade.PersonFacadeLocal;
import ru.ssau.berlin.ejb.facade.UniFacadeLocal;
import ru.ssau.berlin.entity.*;
import ru.ssau.berlin.jsf.util.FacesApplication;

/**
 *
 * @author Caesar
 */

public class PersonView {

    private static Logger logger = Logger.getLogger(PersonView.class.getName());

    @EJB
    private PersonFacadeLocal personFacade;
    @EJB
    private GroupFacadeLocal groupFacade;
    @EJB
    private EventFacadeLocal eventFacade;
    @EJB
    private UniFacadeLocal uniFacade;


    private static final String ID_PARAMETER = "id_param";

    private FacesApplication application = FacesApplication.getInstance();

    private String value;

    private Person person;
    private List<Person> persons;

    private PersonType type;
    private List<PersonType> types;
    private List<SelectItem> items;

    private PersonAttribute attr;
    private List<PersonAttribute> attrs;
    private List<SelectItem> attrItems;

    private List<AttributeType> attrTypes;
    private List<SelectItem> attrTypeItems;

    private List<SelectItem> groupItems;
    private List<SelectItem> eventItems;
    private PersonGroup group;
    private Event event;

    private List<SelectItem> eventRoleItems;
    private List<Event> roleEvents;
    private AttributeType attrType;
    private String roleValue;

    private boolean readonly;

    /** Creates a new instance of PersonView */
    public PersonView() {
    }

    public List<Person> getPersons() {
        return persons;
    }
    
    public List<PersonType> getTypes() {
        return types;
    }

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }

    public boolean isReadonly() {
        return readonly;
    }

    public void setReadonly(boolean readonly) {
        this.readonly = readonly;
    }

    public PersonType getType() {
        return type;
    }

    public void setType(PersonType type) {
        this.type = type;
    }

    public List<SelectItem> getItems() {
        return items;
    }

    public PersonAttribute getAttr() {
        return attr;
    }

    public void setAttr(PersonAttribute attr) {
        this.attr = attr;
    }

    public List<AttributeType> getAttrTypes() {
        return attrTypes;
    }

    public void setAttrTypes(List<AttributeType> attrTypes) {
        this.attrTypes = attrTypes;
    }

    public List<PersonAttribute> getAttrs() {
        return attrs;
    }

    public void setAttrs(List<PersonAttribute> attrs) {
        this.attrs = attrs;
    }

    public List<SelectItem> getAttrItems() {
        return attrItems;
    }

    public List<SelectItem> getAttrTypeItems() {
        return attrTypeItems;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public List<SelectItem> getEventItems() {
        return eventItems;
    }

    public List<SelectItem> getGroupItems() {
        return groupItems;
    }

    public Event getEvent() {
        return event;
    }

    public void setEvent(Event event) {
        this.event = event;
    }

    public PersonGroup getGroup() {
        return group;
    }

    public void setGroup(PersonGroup group) {
        this.group = group;
    }

    public List<SelectItem> getEventRoleItems() {
        return eventRoleItems;
    }

    public List<Event> getRoleEvents() {
        return roleEvents;
    }

    public AttributeType getAttrType() {
        return attrType;
    }

    public void setAttrType(AttributeType attrType) {
        this.attrType = attrType;
    }

    public String getRoleValue() {
        return roleValue;
    }

    public void setRoleValue(String roleValue) {
        this.roleValue = roleValue;
    }

    public String showAllTypes() {
        types = personFacade.findPersonTypes();
        return application.getNavigator().goToPersonTypes();
    }

    public String showPersonType() {
        loadType();
        types = new ArrayList<PersonType>(2);
        if (type.getParent() != null) {
            types.add(type.getParent());
        }
        generatePersonTypeSelectItems();
        readonly = true;
        return application.getNavigator().goToPersonType();
    }

    public String newPersonType() {
        type = new PersonType();
        types = personFacade.findPersonTypes();
        generatePersonTypeSelectItems();
        readonly = false;
        return application.getNavigator().goToPersonType();
    }

    public String persistPersonType() {
        if (type.getId() == null) {
            personFacade.createType(type);
        } else {
            personFacade.editType(type);
        }
        return showAllTypes();
    }

    public String editPersonType() {
        loadType();
        types = personFacade.findPersonTypes();
        removeChildren();
        generatePersonTypeSelectItems();
        attrs = personFacade.findPersonAttributes();
        PersonType currType = type;
        while (currType != null) {
            attrs.removeAll(currType.getAttributes());
            currType = currType.getParent();
        }
        generateAttributeSelectItems();
        readonly = false;
        return application.getNavigator().goToPersonType();
    }

    public void deletePersonType(ActionEvent event) {
        personFacade.removeType(personFacade.findPersonType(Long.parseLong(
                event.getComponent().getAttributes().get(ID_PARAMETER).toString())));
    }

    public String showAllPersons() {
        persons = personFacade.findAll();
        return application.getNavigator().goToPersonsList();
    }

    public String showPerson() {
        logger.log(Level.INFO, "showPerson() entrance");
        person = personFacade.find(Long.parseLong(
                application.getRequestParameter(ID_PARAMETER)));
        logger.log(Level.INFO, "Person found");
        types = new ArrayList<PersonType>(2);
        types.add(person.getType());
        logger.log(Level.INFO, "Types array initialized");
        generatePersonTypeSelectItems();
        logger.log(Level.INFO, "Select Items generates");
        generateGroupItems(getFreeGroups(person));
        generateEventItems(getFreeEvents(person));
        generateEventsForRoles();
        readonly = true;
        return application.getNavigator().goToPersonView();
    }

    public String editPerson() {
        person = personFacade.find(Long.parseLong(
                application.getRequestParameter(ID_PARAMETER)));
        attrs = new LinkedList<PersonAttribute>();
        for (PersonType currType = person.getType(); currType != null; currType = currType.getParent()) {
            attrs.addAll(currType.getAttributes());
        }
        for (PersonParameter param: person.getParameters()) {
            attrs.remove(param.getAttribute());
        }
        generateAttributeSelectItems();
        types = personFacade.findPersonTypes();
        generatePersonTypeSelectItems();
        generateGroupItems(getFreeGroups(person));
        generateEventItems(getFreeEvents(person));
        generateEventsForRoles();
        readonly = false;
        return application.getNavigator().goToPersonView();
    }

    public String newPerson() {
        person = new Person();
        types = personFacade.findPersonTypes();
        generatePersonTypeSelectItems();
        readonly = false;
        return application.getNavigator().goToPersonView();
    }

    public String persistPerson() {
        if (person.getId() == null) {
            personFacade.create(person);
        } else {
            personFacade.edit(person);
        }
        return showAllPersons();
    }

    public String addParameter() {
        PersonParameter param = new PersonParameter();
        param.setAttribute(attr);
        param.setPerson(person);
        param.setParameterValue(value);
        personFacade.createParameter(param);
        person = personFacade.find(person.getId());
        attrs.remove(attr);
        generateAttributeSelectItems();
        value = "";
        return application.getNavigator().stayHere();
    }

    public void deleteParameter(ActionEvent event) {
        PersonParameter param = personFacade.findParameter(Long.parseLong(
                event.getComponent().getAttributes().get(ID_PARAMETER).toString()));
        personFacade.removeParameter(param);
        attrs.add(param.getAttribute());
        generateAttributeSelectItems();
        person = personFacade.find(person.getId());
    }

    public void deletePerson(ActionEvent event) {
        person = personFacade.find(Long.parseLong(
                event.getComponent().getAttributes().get(ID_PARAMETER).toString()));
        personFacade.remove(person);
    }

    private void loadType() {
        type = personFacade.findPersonType(Long.parseLong(
                application.getRequestParameter(ID_PARAMETER)));
    }

    private void generatePersonTypeSelectItems() {
        items = new ArrayList<SelectItem>(types.size());
        for (PersonType iter: types) {
            items.add(new SelectItem(iter, iter.toString()));
        }
    }

    public String showAllAttributes() {
        attrs = personFacade.findPersonAttributes();
        return application.getNavigator().goToPersonAttributes();
    }

    public String showAttribute() {
        loadAttribute();
        attrTypes = uniFacade.findAttributeTypes();
        generateAttributeTypeSelectItems();
        readonly = true;
        return application.getNavigator().goToPersonAttribute();
    }

    public String newAttribute() {
        attr = new PersonAttribute();
        attrTypes = uniFacade.findAttributeTypes();
        generateAttributeTypeSelectItems();
        readonly = false;
        return application.getNavigator().goToPersonAttribute();
    }

    public String addAttribute() {
        type.getAttributes().add(attr);
        personFacade.editType(type);
        attrs = personFacade.findPersonAttributes();
        PersonType currType = type;
        while (currType != null) {
            attrs.removeAll(currType.getAttributes());
            currType = currType.getParent();
        }
        generateAttributeSelectItems();
        return application.getNavigator().stayHere();
    }

    public String persistAttribute() {
        if (attr.getId() == null) {
            personFacade.createAttribute(attr);
            if (type != null) {
                type.getAttributes().add(attr);
                personFacade.editType(type);
            }
        } else {
            personFacade.editAttribute(attr);
        }
        return application.getNavigator().goToPersonType();
    }

    public String unbindAttribute() {
        if (type != null) {
            loadAttribute();
            type.getAttributes().remove(attr);
            personFacade.editType(type);
            attrs = personFacade.findPersonAttributes();
            PersonType currType = type;
            while (currType != null) {
                attrs.removeAll(currType.getAttributes());
                currType = currType.getParent();
            }
            generateAttributeSelectItems();
        }
        return application.getNavigator().stayHere();
    }

    public String editAttribute() {
        loadAttribute();
        attrTypes = uniFacade.findAttributeTypes();
        generateAttributeTypeSelectItems();
        readonly = false;
        return application.getNavigator().goToPersonAttribute();
    }

    public void deleteAttribute(ActionEvent event) {
        attr = personFacade.findPersonAttribute(Long.parseLong(
                event.getComponent().getAttributes().get(ID_PARAMETER).toString()));
        personFacade.removeAttribute(attr);
        attrs = personFacade.findPersonAttributes();
    }

    public void joinGroup(ActionEvent event) {
        person.getGroups().add(group);
        generateGroupItems(getFreeGroups(person));
        personFacade.edit(person);
    }

    public String leaveGroup() {
        group = groupFacade.find(Long.parseLong(
                application.getRequestParameter(ID_PARAMETER)));
        person.getGroups().remove(group);
        personFacade.edit(person);
        generateGroupItems(getFreeGroups(person));
        return application.getNavigator().stayHere();
    }

    public void joinEvent(ActionEvent e) {
        person.getEvents().add(event);
        generateEventItems(getFreeEvents(person));
        personFacade.edit(person);
    }

    public String leaveEvent() {
        event = eventFacade.find(Long.parseLong(
                application.getRequestParameter(ID_PARAMETER)));
        person.getEvents().remove(event);
        personFacade.edit(person);
        generateEventItems(getFreeEvents(person));
        return application.getNavigator().stayHere();
    }

    public void addRole(ActionEvent e) {
        PersonRole role = new PersonRole();
        role.setEvent(event);
        role.setPerson(person);
        role.setRoleValue(roleValue);
        role.setType(attrType);
        uniFacade.createRole(role);
        person.getRoles().add(role);
        generateEventsForRoles();
        roleValue = "";
    }

    public String deleteRole() {
        PersonRole role = uniFacade.findRole(Long.parseLong(
                application.getRequestParameter(ID_PARAMETER).toString()));
        person.getRoles().remove(role);
        uniFacade.removeRole(role);
        generateEventsForRoles();
        return application.getNavigator().stayHere();
    }

    private void loadAttribute() {
        attr = personFacade.findPersonAttribute(Long.parseLong(
                application.getRequestParameter(ID_PARAMETER)));
    }

    private void generateAttributeSelectItems() {
        attrItems = new ArrayList<SelectItem>(attrs.size() + 1);
        for (PersonAttribute attribute: attrs) {
            attrItems.add(new SelectItem(attribute, attribute.getName()));
        }
    }

    private void generateAttributeTypeSelectItems() {
        attrTypeItems = new ArrayList<SelectItem>(attrTypes.size());
        for (AttributeType iter: attrTypes) {
            attrTypeItems.add(new SelectItem(iter, iter.getName()));
        }
    }

    private void generateEventsForRoles() {
         Set<Event> set = new HashSet<Event>();
         set.addAll(person.getEvents());
         for (PersonGroup iter: person.getGroups()) {
             set.addAll(iter.getEvents());
         }
         for (PersonRole iter: person.getRoles()) {
             set.remove(iter.getEvent());
         }
         roleEvents = new ArrayList<Event>(set);
         eventRoleItems = new ArrayList<SelectItem>(roleEvents.size());
         for (Event iter: roleEvents) {
             eventRoleItems.add(new SelectItem(iter, iter.getName()));
         }
    }

    private List<PersonGroup> getFreeGroups(Person person) {
        List<PersonGroup> result = groupFacade.findAll();
        result.removeAll(person.getGroups());
        return result;
    }

    private List<Event> getFreeEvents(Person person) {
        List<Event> result = eventFacade.findAll();
        result.removeAll(person.getEvents());
        return result;
    }

    private void generateGroupItems(List<PersonGroup> groups) {
        groupItems = new ArrayList<SelectItem>(groups.size());
        for (PersonGroup iter: groups) {
            groupItems.add(new SelectItem(iter, iter.getName()));
        }
    }

    private void generateEventItems(List<Event> events) {
        eventItems = new ArrayList<SelectItem>(events.size());
        for (Event iter: events) {
            eventItems.add(new SelectItem(iter, iter.getName()));
        }
    }

    private void removeChildren() {
        List<PersonType> toRemove = new ArrayList<PersonType>(types.size() + 1);
        for (PersonType iter: types) {
            PersonType curr = iter;
            while (curr != null && !curr.equals(type)) {
                curr = curr.getParent();
            }
            if (curr != null) {
                toRemove.add(iter);
            }
        }
        types.removeAll(toRemove);
    }
    
}
